/*******************************************************************************

  Pilot Intelligence Library
    https://gitee.com/pi-lab

*******************************************************************************/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "utils.h"
#include "utils_osa.h"
#include "utils_debug.h"


#ifdef _WIN32 // WIN

typedef int pid_t;

pid_t run_program_(const std::string &cmd)
{
    printf("ERR: Windows do not support run_program_!\n");
    return 0;
}

int stop_program_(pid_t pid)
{
    printf("ERR: Windows do not support stop_program_!\n");
    return 0;
}

int run_program_readbuf(const std::string& cmd, std::string& sout) {
    printf("ERR: Windows do not support run_program_readbuf!\n");
    return 0;
}


#else // LINUX

#include <cstdio>
#include <cstdlib>
#include <cstring>

#include <signal.h>
#include <unistd.h>

////////////////////////////////////////////////////////////////////////////////
/// run program function
////////////////////////////////////////////////////////////////////////////////

pid_t run_program_(const std::string &cmd)
{
    pid_t pid;

    pid = fork();
    if( pid < 0 ) {
        dbg_pe("Can not create new process! ret = %d\n", pid);
        return -1;
    } else if( 0 == pid ) {
        pi::StringArray sa;

        sa = pi::split_text(cmd, " ");
        if( sa.size() > 0 ) {
            char **arg = NULL;
            int  narg = sa.size();
            int  i;

            if( narg > 0 ) {
                arg = (char**) malloc(sizeof(char*)*(narg+1));
                for(i=0; i<narg; i++) {
                    arg[i] = (char*) malloc(sizeof(char)*(sa[i].size()+16));
                    strcpy(arg[i], sa[i].c_str());
                    printf("argv[%2d] = %s\n", i, arg[i]);
                }
                arg[narg] = NULL;
            }

            execv(sa[0].c_str(), arg);
            //system(cmd.c_str());
            dbg_pt("program stopped!");

            if( arg != NULL ) {
                for(i=0; i<narg; i++) {
                    free(arg[i]);
                }
                free(arg);
            }
        }

        exit(0);
    }

    return pid;
}

int stop_program_(pid_t pid)
{
    kill(pid, SIGKILL);
    kill(pid, SIGQUIT);
    kill(pid, SIGTERM);

    return 0;
}

int run_program_readbuf(const std::string& cmd, std::string& sout) {
    sout = "";

    FILE *fp = popen(cmd.c_str(), "r");
    if( fp == NULL ) {
        dbg_pe("Can not run command: %s\n", cmd.c_str());
        return -1;
    }

    // read stdout of the program
    char buf[1024];
    while( fgets(buf, sizeof(buf), fp) != NULL ) {
        sout += buf;
    }

    int status = pclose(fp);
    if( status == -1 ) {
        dbg_pe("pclose filed!");
        return -2;
    }

    // check exit code
    if( WIFEXITED(status) ) {
        int exit_status = WEXITSTATUS(status);
        return exit_status;
    } else {
        dbg_pe("Command terminated abnormally!");
    }

    return -3;
}

#endif


int stop_program_allsubprocess(pid_t pid)
{
    char cmd[1024];
    std::string sout;

    sprintf(cmd, "ps -o pid --ppid %d --noheaders", pid);
    run_program_readbuf(cmd, sout);

    printf("cmd: %s\n", cmd);
    printf("out: %s\n", sout.c_str());

    pi::StringArray sa = pi::split_text(sout, "\n");
    for( auto s : sa ) {
        if( s.size() > 0 ) {
            std::string ss = pi::trim(s);
            printf("pid: %s\n", ss.c_str());

            sprintf(cmd, "kill -9 %s", ss.c_str());
            int ret = system(cmd);
            (void) ret;
        }
    }

    return 0;
}


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

struct RunProgram_InnerData
{
    pid_t   pid;

    RunProgram_InnerData() {
        pid = 0;
    }

    ~RunProgram_InnerData() {
        if( pid > 0 ) stop();
    }

    int run(const std::string &cmd) {
        if( pid > 0 ) stop();

        pid = run_program_(cmd);
        dbg_pt("pid = %d\n", pid);

        return 0;
    }

    int stop(void) {
        if( pid > 0 ) {
            dbg_pt("pid = %d\n", pid);
            //stop_program_(pid);
            stop_program_allsubprocess(pid);
            pid = 0;
        }

        return 0;
    }
};


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

namespace pi {

int RunProgram::run(const std::string &cmd)
{
    RunProgram_InnerData *id = (RunProgram_InnerData*) m_data;

    // stop old program
    stop();

    m_cmd = cmd;

    if( id != NULL ) {
        id->run(cmd);
    }

    return 0;
}

void RunProgram::stop(void)
{
    RunProgram_InnerData *id = (RunProgram_InnerData*) m_data;

    // stop running program
    if( id != NULL ) {
        id->stop();
    }
}


int RunProgram::init(void)
{
    RunProgram_InnerData *id = new RunProgram_InnerData;
    m_data = id;

    return 0;
}

int RunProgram::release(void)
{
    stop();

    RunProgram_InnerData *id = (RunProgram_InnerData*) m_data;
    if( id != NULL ) {
        delete id;
        m_data = NULL;
    }

    return 0;
}

} // end of namespace pi
